/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *	  http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.milib.astro.threeD {
	import com.googlecode.milib.actionControll.observe.AnyActionIsRunningObserver;
	import com.googlecode.milib.actionControll.observe.WhenAnyActionRunningEnterFrame;
	import com.googlecode.milib.animation.dynamic_.AnimatedStoppingNumber;
	import com.googlecode.milib.base.MLActionClass;
	import com.googlecode.milib.util.MLMCUtil;
	import com.googlecode.milib.vo.MLEvent;
	
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;	

	/**
	 * action start - camera is started changing position (start animation)
	 * action finish - camera is finish changing position (stop animation)
	 * 
	 * @author Marek Brun
	 */
	public class Camera3D extends MLActionClass {
		
		private var _scene:DisplayObjectContainer;
		private var animX:AnimatedStoppingNumber;
		private var animY:AnimatedStoppingNumber;
		private var animZ:AnimatedStoppingNumber;
		private var animRX:AnimatedStoppingNumber;
		private var animRY:AnimatedStoppingNumber;
		private var animRZ:AnimatedStoppingNumber;
		private var anyAnimationRunning:AnyActionIsRunningObserver;		public var isTween:Boolean=false;		public var isZSortingWhenTween:Boolean=false;
		private var whenAnimRunningEnterFrame:WhenAnyActionRunningEnterFrame;

		public function Camera3D(scene:DisplayObjectContainer, animSpeed:Number=.5) {
			_scene=scene;
			if(!scene.transform.matrix3D){
				scene.z=-1;
				scene.z=0;
			}
			
			animX=new AnimatedStoppingNumber(scene.x, 0.5, animSpeed);
			animX.addAutoMapping(scene, 'x');
			animY=new AnimatedStoppingNumber(scene.y, 0.5, animSpeed);
			animY.addAutoMapping(scene, 'y');
			animZ=new AnimatedStoppingNumber(scene.z, 0.5, animSpeed);
			animZ.addAutoMapping(scene, 'z');
			animRX=new AnimatedStoppingNumber(scene.rotationX, 0.5, animSpeed);
			animRX.addAutoMapping(scene, 'rotationX');
			animRY=new AnimatedStoppingNumber(scene.rotationY, 0.5, animSpeed);
			animRY.addAutoMapping(scene, 'rotationY');
			animRZ=new AnimatedStoppingNumber(scene.rotationZ, 0.5, animSpeed);
			animRZ.addAutoMapping(scene, 'rotationZ');
			
			anyAnimationRunning=new AnyActionIsRunningObserver();
			anyAnimationRunning.collection.addValues([animX, animY, animZ, animRX, animRY, animRZ]);
			anyAnimationRunning.addEventListener(anyAnimationRunning.event_SomeActionIsRunning, onAnyAnimationRunning_SomeActionIsRunning);			anyAnimationRunning.addEventListener(anyAnimationRunning.event_AllActionsIsNotRunning, onAnyAnimationRunning_AllActionsIsNotRunning);
			
			whenAnimRunningEnterFrame=new WhenAnyActionRunningEnterFrame([this]);
			whenAnimRunningEnterFrame.addEventListener(whenAnimRunningEnterFrame.event_EnterFrameWhenAnyActionRunning, onWhenAnimRunningEnterFrame);
			
			zsort();
			
		}
		
		public function zsort():void {
			var childs:Array=MLMCUtil.getChilds(scene);
			var i:uint;
			var child:DisplayObject, matrix:Matrix3D, childsAndZ:Array=[];
			for(i=0;i<childs.length;i++){
				child=childs[i];
				if(!child.transform.matrix3D){ continue; }
				
				matrix=child.transform.matrix3D.clone();
				matrix.append(scene.transform.matrix3D);
				
				childsAndZ.push({z:matrix.position.z, child:child});
				scene.removeChild(child);
				
			}
			childsAndZ.sortOn('z', Array.NUMERIC);
			childsAndZ.reverse();
			for(i=0;i<childsAndZ.length;i++){
				child=childsAndZ[i].child;
				scene.addChild(child);
			}
		}
		
		public function lookAt(targetDisplay:DisplayObject):void {
			if(targetDisplay.parent!=scene){
				throw new Error('Passed "targetDisplay" DisplayObject must be child of camera scene');
			}
			scene.transform.matrix3D.interpolateTo(targetDisplay.transform.matrix3D, 1);
			scene.transform.matrix3D.invert();
			
			zsort();
			
			if(isTween) {
				animX.target=scene.x;
				animX.start();
				animY.target=scene.y;
				animY.start();
				animZ.target=scene.z;
				animZ.start();
				animRX.target=scene.rotationX;
				animRX.start();
				animRY.target=scene.rotationY;
				animRY.start();
				animRZ.target=scene.rotationZ;
				animRZ.start();
			}else{
				animX.current=scene.x;
				animY.current=scene.y;
				animZ.current=scene.z;
				animRX.current=scene.rotationX;
				animRY.current=scene.rotationY;
				animRZ.current=scene.rotationZ;
			}
			
		}
		
		public function get scene():DisplayObjectContainer { return _scene; }
		
//********************************************************************************************
//		events for Camera3D
//********************************************************************************************
		protected function onAnyAnimationRunning_SomeActionIsRunning(event:MLEvent):void {
			start();
		}
		
		protected function onAnyAnimationRunning_AllActionsIsNotRunning(event:MLEvent):void {
			finish();
		}
		
		protected function onWhenAnimRunningEnterFrame(event:MLEvent):void {
			if(isZSortingWhenTween){
				zsort();
			}
		}
		
	}
}
